
--[[
title: 定时器
description: 定时器及异步执行
]]

local Timer = commonlib.inherit(nil);

local s_coroutine_timers = {};
local s_timer_coroutines = {};

local _G_coroutine_create = _G.coroutine.create;
local _G_coroutine_yield = _G.coroutine.yield;
local _G_coroutine_resume = _G.coroutine.resume;
local _G_coroutine_isyieldable = _G.coroutine.isyieldable
local _G_coroutine_running = _G.coroutine.running;

-- 屏蔽掉全局协程对象, 防止使用不当出错, 使用封装函数替代相关功能
-- _G.coroutine = nil;

local function coroutine_create(fn)
    local cur_co = _G_coroutine_running();
    local hook, mask, count = debug.gethook(cur_co);
    local co = _G_coroutine_create(fn);
    debug.sethook(co, hook, mask, count);
    return co;
end

function Timer:ctor()
    self.m_timeout = nil;
    self.m_interval = nil;
    self.m_next_activate_timestamp = nil;
    self.m_callback = nil;
end

function Timer:Init(timeout, interval, callback)
    self.m_timeout = timeout;
    self.m_interval = interval;
    self.m_callback = commonlib.xpcall_callback(callback);
    local timer_coroutine = coroutine_create(function()
        commonlib.debug("start coroutine:", _G_coroutine_running());
        self.m_callback(self);
        while (self.m_interval) do
            _G_coroutine_yield();
            self.m_callback(self);
        end
        self:Stop();
        commonlib.debug("end coroutine:", _G_coroutine_running());
    end);
    s_coroutine_timers[timer_coroutine] = self;
    s_timer_coroutines[self] = timer_coroutine;
    return self;
end

function Timer:Refresh(curtime)
    if (not self.m_timeout and not self.m_interval) then return end
    local interval = self.m_timeout or self.m_interval;
    self.m_next_activate_timestamp = curtime + interval;
    self.m_timeout = nil;
    return self;
end

function Timer:Start()
    self:Refresh(commonlib.GetTimeStamp());
    return self;
end

function Timer:Stop()
    self.m_timeout = nil;
    self.m_interval = nil;
    local timer_coroutine = s_timer_coroutines[self];
    if (timer_coroutine) then s_coroutine_timers[timer_coroutine] = nil end
    s_timer_coroutines[self] = nil;
end

function Timer:Activate(curtime)
    local timer_coroutine = s_timer_coroutines[self];
    if (not timer_coroutine or not self.m_next_activate_timestamp or curtime < self.m_next_activate_timestamp) then return end
    -- 先刷新时间戳后激活回调, 方便在回调更新新的时间戳, 同时也可以感知下一次激活时间
    self:Refresh(curtime);
    -- 激活回调
    local ok, errmsg = _G_coroutine_resume(timer_coroutine);
    if (not ok) then
        self:Stop();
        print("Timer:Activate Failed", errmsg);
    end
end

commonlib.SetTimeout = function(timeout, callback)
    return Timer:new():Init(timeout or 0, nil, callback):Start();
end

commonlib.ClearTimeout = function(timer)
    timer:Stop();
end

commonlib.SetInterval = function(interval, callback)
    return Timer:new():Init(nil, interval or 0, callback):Start();
end

commonlib.ClearInterval = function(timer)
    if (not timer) then
        timer:Stop();
    end
end

function commonlib.sleep(timeout)
    if (not timeout or timeout == 0) then timeout = 1 end
    if (not _G_coroutine_isyieldable()) then
        print("ERROR: thread can not yield!!!", _G_coroutine_running());
        return commonlib.exit();
    end
    local cur_co = _G_coroutine_running();
    local cur_timer = s_coroutine_timers[cur_co];
    if (not cur_timer) then
        print("ERROR: current coroutine can not yield!!!", _G_coroutine_running());
        return;
    end
    local cur_timestamp = commonlib.GetTimeStamp();
    local sleep_to = cur_timestamp + timeout;
    local old_timer_interval = cur_timer.m_interval;
    local old_timer_next_activate_timestamp = cur_timer.m_next_activate_timestamp;

    cur_timer.m_interval = 0;
    cur_timer.m_next_activate_timestamp = sleep_to;
    while (cur_timestamp < sleep_to) do
        _G_coroutine_yield();
        cur_timestamp = commonlib.GetTimeStamp();
    end
    cur_timer.m_interval = old_timer_interval;
    cur_timer.m_next_activate_timestamp = old_timer_next_activate_timestamp;
end

-- 异步执行函数
function commonlib.run(callback, ...)
    local args = table.pack(...);
    return commonlib.SetTimeout(0, function()
        callback(table.unpack(args));
    end);
end

-- 异步回调
function commonlib.async_callback(callback)
    local xpcall_callback = commonlib.xpcall_callback(callback);
    local co = coroutine_create(function()
        while (true) do
            xpcall_callback(_G_coroutine_yield());
        end
    end);
    _G_coroutine_resume(co);
    return function(...)
        return _G_coroutine_resume(co, ...);
    end
end

-- 定时器回调
function commonlib.OnTimer()
    local curtime = commonlib.GetTimeStamp();
    -- 拷贝一份遍历列表，避免遍历过程中删除元素
    local timers = commonlib.values(s_coroutine_timers);
    for _, timer in ipairs(timers) do
        timer:Activate(curtime);
    end
end

-- C++ CallBack
_G.__on_timer__ = function()
    commonlib.OnTimer();
end
